/*
  File: ServiceCollectionExtension.cs
  Author: rcrxy
  CreateTime: 2025/07/03 09:59

  Description:
*/

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using StackExchange.Redis;
using StudentVideo.Data;
using StudentVideo.Filters;
using System.Reflection;
using System.Text;

namespace StudentVideo.Extensions;

public static class ServiceCollectionExtension {

   /// <summary>
   /// 连接 Redis
   /// </summary>
   /// <param name="serviceDescriptors"></param>
   /// <param name="configuration"></param>
   /// <returns></returns>
   public static IServiceCollection AddRedisDb(this IServiceCollection serviceDescriptors, IConfiguration configuration) {
      // 连接 redis
      serviceDescriptors.AddSingleton<IConnectionMultiplexer>(sp =>
         ConnectionMultiplexer.Connect(configuration["Redis:ConnectionString"]!)
      );

      // 注册 IDatabase（每次请求获取）
      serviceDescriptors.AddScoped(sp => {
         var multiplexer = sp.GetRequiredService<IConnectionMultiplexer>();
         // 可根据需要指定数据库编号

         var getdb = int.TryParse(configuration["Redis:DefaultDatebase"], out int db);

         if (!getdb)
            db = 0;

         return multiplexer.GetDatabase(db);
      });

      return serviceDescriptors;
   }

   /// <summary>
   /// 动态注册所有的 Interceptor(拦截器)
   /// </summary>
   /// <param name="serviceDescriptors"></param>
   /// <returns></returns>
   public static IServiceCollection AddCustomInterceptors(this IServiceCollection serviceDescriptors) {
      var interceptors = AppDomain.CurrentDomain.GetAssemblies()
         .SelectMany(assembly => assembly.GetTypes())
         .Where(e => {
            return e.IsClass && !e.IsAbstract && e.Name.EndsWith("Interceptor") && typeof(SaveChangesInterceptor).IsAssignableFrom(e);
         });

      foreach (var interceptor in interceptors) {
         serviceDescriptors.AddScoped(interceptor);
         serviceDescriptors.AddScoped(typeof(SaveChangesInterceptor), interceptor);
      }

      return serviceDescriptors;
   }

   /// <summary>
   /// 注册数据库
   /// </summary>
   /// <param name="serviceDescriptors"></param>
   /// <param name="configuration"></param>
   /// <returns></returns>
   public static IServiceCollection AddCustomDatabase(this IServiceCollection serviceDescriptors, IConfiguration configuration) {
      // 注册数据库
      serviceDescriptors.AddDbContext<AppDbContext>((sp, options) => {
         List<SaveChangesInterceptor> interceptors = [.. sp.GetServices<SaveChangesInterceptor>()];

         options.UseMySql(
            configuration.GetConnectionString("DefaultConnection"),
            ServerVersion.AutoDetect(configuration.GetConnectionString("DefaultConnection")),
            mySqlOptions => {
               mySqlOptions.SchemaBehavior(MySqlSchemaBehavior.Ignore);
               mySqlOptions.UseMicrosoftJson();
            }
         )
         .AddInterceptors(interceptors);
      });

      return serviceDescriptors;
   }

   /// <summary>
   /// 动态加载所有的自定义 Service
   /// </summary>
   /// <param name="serviceDescriptors"></param>
   /// <returns></returns>
   public static IServiceCollection AddCustomServices(this IServiceCollection serviceDescriptors) {
      var featureServices = Assembly.GetExecutingAssembly()
         .GetTypes()
         .Where(t => t.IsClass && !t.IsAbstract && t.Name.EndsWith("Service"));

      foreach (var service in featureServices) {
         serviceDescriptors.AddScoped(service);
      }

      return serviceDescriptors;
   }

   /// <summary>
   /// 添加 swagger
   /// </summary>
   /// <param name="serviceDescriptors"></param>
   /// <returns></returns>
   public static IServiceCollection AddOpenApiSwagger(this IServiceCollection serviceDescriptors) {

      serviceDescriptors.AddEndpointsApiExplorer();
      serviceDescriptors.AddSwaggerGen(c => {
         c.SwaggerDoc("v1", new OpenApiInfo { Title = "StudentVideo API", Version = "v1" });
         c.SupportNonNullableReferenceTypes();

         try {
            // 加载 XML 注释文件
            c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"),
               true);
            c.IncludeXmlComments(
               Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.Shared.xml"), true);
         } catch (Exception ex) {
            Console.WriteLine($"swagger 读取 xml 失败: ");
            Console.Write(ex);
         }

         c.OperationFilter<HttpResultOperationFilter>();
      });

      return serviceDescriptors;
   }

   /// <summary>
   /// 添加身份验证服务
   /// </summary>
   /// <param name="serviceDescriptors"></param>
   /// <param name="configuration"></param>
   /// <returns></returns>
   public static IServiceCollection AddCustomAuthentication(this IServiceCollection serviceDescriptors, IConfiguration configuration) {

      serviceDescriptors
         .AddAuthentication(options => {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
         })
         .AddJwtBearer(options => {
            // 读取 JWT 配置
            IConfigurationSection jwtSettings = configuration.GetSection("Jwt");

            string SecretKey = jwtSettings["SecretKey"]!;

            // 配置 JWT 验证参数
            options.TokenValidationParameters = new TokenValidationParameters {
               ValidateIssuer = true,
               ValidateAudience = true,
               ValidateLifetime = true,
               ValidateIssuerSigningKey = true,
               ValidIssuer = jwtSettings["Issuer"],
               ValidAudience = jwtSettings["Audience"],
               ClockSkew = TimeSpan.FromMinutes(5), // 允许本地时间与服务器时间有5分钟偏差
               IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey))
            };

            // 增加日志记录
            options.Events = new JwtBearerEvents {
               OnTokenValidated = context => {
                  return Task.CompletedTask;
               },
               OnAuthenticationFailed = context => {
                  Console.WriteLine($"Authentication failed: {context.Exception}");
                  return Task.CompletedTask;
               }
            };
         });

      return serviceDescriptors;
   }

}
